package org.xbl.xchain.sdk.types;

import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.bouncycastle.util.encoders.Hex;
import org.xbl.xchain.sdk.amino.BytesInterface;
import org.xbl.xchain.sdk.crypto.algo.Algorithm;
import org.xbl.xchain.sdk.crypto.algo.AlgorithmFactory;
import org.xbl.xchain.sdk.crypto.algo.AlgorithmType;
import org.xbl.xchain.sdk.utils.AddressUtil;
import org.xbl.xchain.sdk.utils.PubkeyUtil;

import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Data
public class KeyInfo {
    private String mnemonic;
    private PrivateKey privateKey;
    private PublicKey publicKey;
    private String address;
    private Algorithm algorithm;
    //根据助记词生成KeyInfo(字段：私钥、公钥及地址)方法

    public KeyInfo(String mnemonic, AlgorithmType algorithmType, String mainPrefix) {
        this.mnemonic = mnemonic;
        this.algorithm = AlgorithmFactory.getAlgorithm(algorithmType);
        try {
            this.privateKey = algorithm.genPrivateKeyFromMnemonic(mnemonic);
            this.publicKey = algorithm.genPublicKey(this.privateKey);
            this.address = algorithm.genAddressFromPublicKey(mainPrefix, publicKey);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }

    public KeyInfo(String mnemonic, AlgorithmType algorithmType) {
        this(mnemonic, algorithmType, Address.Bech32MainPrefix);
    }

    public BytesInterface getPubKeyBytes() throws Exception {
        return new BytesInterface(algorithm.parsePubKey(this.publicKey), algorithm.getType().getPubType());
    }

    public String getConsensusPubKey() throws Exception {
        return PubkeyUtil.Bech32ifyPubKey(Address.GetBech32ConsensusPubPrefix(), getPubKeyBytes());
    }

    public String getBech32PubKey() throws Exception {
        return PubkeyUtil.Bech32ifyPubKey(Address.GetBech32AccountPubPrefix(), getPubKeyBytes());
    }

    public String getValidatorAddress() throws Exception {
        return algorithm.genAddressFromPublicKey(Address.GetBech32ValidatorAddrPrefix(), publicKey);
    }
    public String toNodeKeyFile() {
        BigInteger bigInteger = algorithm.getBigIntegerFromMnemonic(this.mnemonic);
        byte[] privkeyByte = new byte[32];
        if(bigInteger.toByteArray().length==33){
            System.arraycopy(bigInteger.toByteArray(),1,privkeyByte,0,32);
        }else{
            System.arraycopy(bigInteger.toByteArray(),0,privkeyByte,0,32);
        }
        Map prikey = new HashMap();
        prikey.put("type", this.algorithm.getType().getPriType());
        prikey.put("value", Base64.getEncoder().encodeToString(privkeyByte));
        Map nodeKeyFile = new HashMap<>();
        nodeKeyFile.put("priv_key", prikey);
        return JSON.toJSONString(nodeKeyFile);
    }
    public String toPrivValidatorKeyFile() throws Exception {
        Map privFile = new HashMap();
        privFile.put("address", Hex.toHexString(AddressUtil.accAddressFromBech32(this.address)));

        BigInteger bigInteger = algorithm.getBigIntegerFromMnemonic(this.mnemonic);

        Map prikey = new HashMap();
        byte[] privkeyByte = new byte[32];
        if(bigInteger.toByteArray().length==33){
            System.arraycopy(bigInteger.toByteArray(),1,privkeyByte,0,32);
        }else{
            System.arraycopy(bigInteger.toByteArray(),0,privkeyByte,0,32);
        }
        prikey.put("type", this.algorithm.getType().getPriType());
        prikey.put("value", Base64.getEncoder().encodeToString(privkeyByte));
        privFile.put("priv_key", prikey);

        Map pubkey = new HashMap();
        pubkey.put("type", this.algorithm.getType().getPubType());
        pubkey.put("value", Base64.getEncoder().encodeToString(algorithm.parsePubKey(this.publicKey)));
        privFile.put("pub_key", pubkey);

        return JSON.toJSONString(privFile);
    }
    public String getNodeId() throws Exception {
        byte[] bytes = AddressUtil.accAddressFromBech32(this.address);
        return Hex.toHexString(bytes);
    }

    public byte[] sign(byte[] msg) throws Exception {
        return this.algorithm.sign(this.privateKey, msg);
    }
}
